package edu.mcw.rgd.clinminer.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.PostPersist;
import javax.persistence.PreRemove;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.apache.log4j.Logger;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.plural.RooPlural;
import org.springframework.roo.addon.tostring.RooToString;

import edu.mcw.rgd.clinminer.domain.common.Persistable;

@RooJavaBean
@RooToString
@RooPlural("Studies")
@RooJpaActiveRecord(persistenceUnit = "persistenceUnit")
@Table(name = "STUDY")
public class Study implements Persistable {
	private static Logger log = Logger.getLogger(Study.class);

	@Id
	@GeneratedValue(generator = "Study_IDs_Seq")
	@SequenceGenerator(name = "Study_IDs_Seq", sequenceName = "Study_IDs_Seq", allocationSize = 1)
	@Column(name = "study_id")
	private Long id;

	@NotNull(message = "Name cannot be empty")
	@Column(name = "name")
	private String name;

	@Column(name = "description")
	private String description;

	@ManyToMany
	@JoinTable(name = "study_participants", joinColumns = @JoinColumn(name = "studies"), inverseJoinColumns = @JoinColumn(name = "participants"))
	private Set<Participant> participants = new HashSet<Participant>();

	@PreRemove
	private void preRemove() {
		for (Participant p : participants) {
			p.getStudies().remove(this);
			p.merge();
		}
	}

	public void setParticipants(Set<Participant> participants) {
		// calculate asymmetric set differences non-destructively
		Set<Participant> removedParticipants = new HashSet<Participant>(
				this.participants);
		removedParticipants.removeAll(participants);
		Set<Participant> addedParticipants = new HashSet<Participant>(
				participants);
		addedParticipants.removeAll(this.participants);

		this.participants = participants;

		// need to maintain bidirectional manually
		for (Participant p : addedParticipants) {
			p.getStudies().add(this);
			p.merge();
		}

		for (Participant p : removedParticipants) {
			p.getStudies().remove(this);
			p.merge();
		}
	}

	@PostPersist
	// only adding here
	private void syncBidirectionalPostPersist() {
		for (Participant p : participants) {
			p.getStudies().add(this);
			p.merge();
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Study other = (Study) obj;
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		return true;
	}

}
